package xj.model.uml;

import static xj.util.xml.XMLUtil.getXMLAttributeString;
import static xj.util.xml.XMLUtil.parseXMLDocument;
import static xj.util.xml.XMLUtil.stripWhiteSpace;

import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import xj.meta.Stereotype;
import xj.util.xml.ErrorMessageHandler;

public class Profile {

  public Profile() {
  }

  public Profile(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Set<DataType> getDataTypes() {
    return datatypes;
  }

  public void addDataType(DataType d) {
    if (d != null) {
      datatypes.add(d);
    }
  }

  public void removeDataType(DataType d) {
    if (d != null) {
      datatypes.remove(d);
    }
  }

  public Set<Stereotype> getStereotypes() {
    return stereotypes;
  }

  public void addStereotype(Stereotype d) {
    if (d != null) {
      stereotypes.add(d);
    }
  }

  public void removeStereotype(Stereotype d) {
    if (d != null) {
      stereotypes.remove(d);
    }
  }

  public Set<String> getAvailableStereotypes(String name) {
    if (name != null) {
      return availableStereotypes.get(name);
    }
    return null;
  }

  public void addAvailableStereotype(String name, String stereotype) {
    if (name != null && stereotype != null) {
      Set<String> set = availableStereotypes.get(name);
      if (set != null) {
	set.add(stereotype);
      } else {
	set = new HashSet<String>();
	set.add(stereotype);
	availableStereotypes.put(name, set);
      }
    }
  }

  public void addAvailableStereotypes(String name, Set<String> stereotypes) {
    if (name != null && 
	stereotypes != null && 
	!stereotypes.isEmpty()) {
      Set<String> set = availableStereotypes.get(name);
      if (set != null) {
	set.addAll(stereotypes);
      } else {
	set = new HashSet<String>();
	set.addAll(stereotypes);
	availableStereotypes.put(name, set);
      }
    }
  }

  public void save(String filename) {
    if (filename != null) {
      try {
	PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
	out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>");
	if (name != null && name.length() > 0) {
	  out.print("<Profile " + getXMLAttributeString("name", name) + ">");
	} else {
	  out.print("<Profile>");
	}

	if (stereotypes != null && stereotypes.size() > 0) {
	  Iterator<Stereotype> iter = stereotypes.iterator();
	  while (iter.hasNext()) {
	    Stereotype st = iter.next();
	    if (st != null) {
	      String stname = st.getName();
	      if (stname != null && stname.length() > 0) {
		Map<String, String> tags = st.getTags();
		if (tags == null || tags.isEmpty()) {
		  out.println(" <Stereotype " + 
			      getXMLAttributeString("name", stname) + "/>");
		} else {
		  out.println(" <Stereotype " + 
			      getXMLAttributeString("name", stname) + ">");
		  Iterator<String> titer = tags.keySet().iterator();
		  while (titer.hasNext()) {
		    String tname = titer.next();
		    String tvalue = tags.get(tname);
		    out.println("  <Tag " + getXMLAttributeString("name", tname) + 
				getXMLAttributeString("value", tvalue) + "/>");
		  }
		  out.println(" </Stereotype>");
		}
	      }
	    }
	  }
	}

	if (datatypes != null && datatypes.size() > 0) {
	  Iterator<DataType> iter = datatypes.iterator();
	  while (iter.hasNext()) {
	    DataType dt = iter.next();
	    if (dt != null) {
	      String dtname = dt.getName();
	      if (dtname != null && dtname.length() > 0) {
		out.println(" <DataType " + 
			    getXMLAttributeString("name", dtname) + "/>");
	      }
	    }
	  }
	}

	if (availableStereotypes != null && availableStereotypes.size() > 0) {
	  Iterator<String> iter = availableStereotypes.keySet().iterator();
	  while (iter.hasNext()) {
	    String ename = iter.next();
	    Set<String> st = availableStereotypes.get(ename);
	    Iterator<String> siter = st.iterator();
	    while (siter.hasNext()) {
	      String sname = siter.next();
	      out.println(" <AvailableSteroetype " + 
			  getXMLAttributeString("element", ename) + 
			  getXMLAttributeString("name", sname) + "/>");
	    }
	  }
	}

	out.println("</Profile>");
	out.close();
      } catch (IOException e) {
	System.out.println("Unable to write file: " + filename);
	// e.printStackTrace();
      } catch (Exception e) {
	System.out.println("Unable to write file: " + filename);
	// e.printStackTrace();
      }

    }
  }

  public void load(String filename) {
    if (filename != null) {
      try {
	InputStream in = null;
	in = new FileInputStream(filename);
	org.w3c.dom.Document doc = null;
	if (in != null) {
	  ErrorMessageHandler errorHandler = new ErrorMessageHandler(filename);
	  doc = parseXMLDocument(in, errorHandler, false);
	  if (errorHandler.hasErrorOrWarning()) {

	  }
	}

	if (doc != null) {
	  Element docElement = doc.getDocumentElement();
	  stripWhiteSpace(docElement);
	  if ("Profile".equals(docElement.getLocalName())) {
	    name = docElement.getAttribute("name");
	    if (docElement.hasChildNodes()) {
	      NodeList nodes = docElement.getChildNodes();
	      if (nodes != null) {
		int n = nodes.getLength();
		for (int i = 0; i < n; i++) {
		  Node node = nodes.item(i);
		  if (node.getNodeType() == Node.ELEMENT_NODE) {
		    Element e = (Element) node;
		    String name = e.getLocalName();
		    if ("AvailableSteroetype".equals(name)) {
		      String element = e.getAttribute("element");
		      String stname = e.getAttribute("name");
		      if (element != null && 
			  element.length() > 0  && 
			  stname != null && 
			  stname.length() > 0) {
			addAvailableStereotype(element, stname);
			// System.out.println("add
			// stereotype: " + element + " "
			// + name);
		      }
		    } else if ("Stereotype".equals(name)) {
		      String stname = e.getAttribute("name");
		      if (stname != null && stname.length() > 0) {
			Stereotype st = new Stereotype(name);
			if (e.hasChildNodes()) {
			  // tagged values
			  NodeList tagnodes = e.getChildNodes();
			  if (tagnodes != null) {
			    int tagcount = nodes.getLength();
			    for (int j = 0; j < tagcount; j++) {
			      Node tnode = tagnodes.item(j);
			      if (tnode.getNodeType() == Node.ELEMENT_NODE) {
				Element te = (Element) tnode;
				String tname = te.getLocalName();
				if ("Tag".equals(tname)) {
				  String tagname = te.getAttribute("name");
				  String tagvalue = te.getAttribute("value");
				  if (tagname != null && 
				      tagname.length() > 0) {
				    st.setTagValue(tagname, tagvalue);
				  }
				}
			      }
			    }
			  }
			}
			addStereotype(st);
		      }
		    } else if ("DateType".equals(name)) {
		      String dtname = e.getAttribute("name");
		      if (dtname != null && 
			  dtname.length() > 0) {
			DataType dt = new DataType(name);
			addDataType(dt);
		      }
		    } else {

		    }
		  }
		}
	      }
	    }
	  }
	}

      } catch (IOException e) {
	System.err.println("Error reading file " + filename + ": "
	    + e.getMessage());
	if (debug) {
	  e.printStackTrace(System.err);
	}
      } catch (SAXParseException e) {
	// error in input
	System.err.println("Error " + filename + ":" + e.getMessage());
	if (debug) {
	  e.printStackTrace(System.err);
	}
      } catch (SAXException e) {
	// error in input
	System.err.println("Error " + filename + ":" + e.getMessage());
	if (debug) {
	  e.printStackTrace(System.err);
	}
      }
    }

  }

  protected String name;

  protected Map<String, Set<String>> availableStereotypes = new HashMap<String, Set<String>>();

  protected Set<DataType> datatypes = new HashSet<DataType>();

  protected Set<Stereotype> stereotypes = new HashSet<Stereotype>();

  protected static final boolean debug = true;

}
